(function (win) {
  var setORP = function (o, p, v, e) {
    if (isStr(p)) { return Object.defineProperty(o, p, { configurable: false, enumerable: (isVoid(e) ? true : !!e), writable: false, value: v }); }
    var en = isVoid(v) ? true : !!v, po = {};
    each(p, function (k, v) { po[k] = { configurable: false, enumerable: en, writable: false, value: v }; });
    Object.defineProperties(o, po);
  };

  $.extend({
    TQ: function (o) { return (o ? TQ.opt(o) : TQ.start()); },
    log: msg => { console.log(msg); }
  });

  setORP($.TQ, {
    clear: function () { tq = { stateFn: 0, sleep: 50, optSleep: 0, arrSleep: [], arrTask: [], curTask: 0, curRet: 0, isStop: 0, states: [], globaOpt: {}, overFn: 0, isOptTaskArr: 0 }; return TQ; },
    opt: function (o) { //o.sleep, o.opt, o.state, o.over, o.tq
      TQ.clear();
      o.state && (tq.stateFn = o.state);
      o.over && (tq.overFn = o.over);
      o.sleep && (tq.optSleep = o.sleep);
      o.tq && (TQ.add(o.tq)) && (tq.isOptTaskArr = $.isArr(o.tq));
      if (o.opt) { for (var p in o.opt) { if (o.opt.hasOwnProperty(p)) { tq.globaOpt[p] = o.opt[p]; } } }
      return TQ;
    },
    add: function (o) { for (var i = 0, a = toArr(o); i < a.length; i++) { tq.arrTask.push($.isFn(a[i]) ? { do: a[i] } : a[i]) } return TQ; },
    start: function (fn) {
      (fn = function (to) {
        if (tq.arrTask.length < 1) return TQ.clear();
        if (tq.isStop) return TQ;
        to = tq.curTask = tq.arrTask.shift();
        if (!to.hasShowState) {
          to.hasShowState = 1;
          if (tq.stateFn && (tq.isOptTaskArr || !$.isNope(to.state))) { tq.stateFn(to.state || '', tq.curRet, tq.curTask.opt, tq.globaOpt); tq.arrTask.unshift(to); return setTimeout(fn, 20); }
        }
        if (!to.hasDone) {
          if (to.do) {
            try {
              tq.curRet = to.do(tq.curRet, tq.curTask.opt, tq.globaOpt);
            } finally { to.over && (to.over()); tq.overFn && tq.overFn(tq.curRet, tq.curTask.opt, tq.globaOpt); }
          }
          to.hasDone = 1;
        }
        to.clearIsGoon && (to.clearIsGoon = to.isGoon = 0);
        !to.goon && (to.isGoon = 1);
        if (!to.isGoon) {
          try {
            to.isGoon = to.goon(tq.curRet, tq.curTask.opt, tq.globaOpt);
          } finally { to.over && (to.over()); tq.overFn && tq.overFn(tq.curRet, tq.curTask.opt, tq.globaOpt); }
          !to.isGoon && (TQ.redo(1));
        }
        to.isGoon && tq.stateFn && !$.isNope(to.overState) && (tq.stateFn(to.overState, tq.curRet, tq.curTask.opt, tq.globaOpt));
        setTimeout(fn, (tq.arrSleep.pop() || tq.optSleep || tq.sleep));
      })();
      return TQ;
    },
    stop: function () { tq.isStop = 1; return TQ; },
    redo: function (isGoonOnly) { if (!isGoonOnly) { tq.curTask.clearIsGoon = 1; tq.curTask.hasDone = 0; } tq.arrTask.unshift(tq.curTask); return TQ; },
    sleep: function (o) { tq.arrSleep.push(o); return TQ; },
    size: function () { return tq.arrTask.length; }
  });

  // UI
  var $$ = {};
  $(function () {
    $.each($('[id]'), function (i, v) { $$[v.id] = $('#' + v.id); });
  });
  win.$$ = $$;
})(window);